AtklÄjiet spÄcÄ«gu lietojumprogrammu droŔību ar mÅ«su tipdroÅ”as autorizÄcijas ceļvedi. Ieviesiet sistÄmu kļūdu novÄrÅ”anai, pieredzes uzlaboÅ”anai un mÄrogojamai piekļuves kontrolei.
Koda stiprinÄÅ”ana: padziļinÄts ieskats tipdroÅ”Ä autorizÄcijÄ un atļauju pÄrvaldÄ«bÄ
ProgrammatÅ«ras izstrÄdes sarežģītajÄ pasaulÄ droŔība nav funkcija; tÄ ir fundamentÄla prasÄ«ba. MÄs veidojam ugunsmÅ«rus, Å”ifrÄjam datus un aizsargÄjam pret injekcijÄm. TomÄr izplatÄ«ta un viltÄ«ga ievainojamÄ«ba bieži vien slÄpjas atklÄti, dziļi mÅ«su lietojumprogrammu loÄ£ikÄ: autorizÄcija. PrecÄ«zÄk, veids, kÄdÄ mÄs pÄrvaldÄm atļaujas. Gadiem ilgi izstrÄdÄtÄji ir paļÄvuÅ”ies uz Ŕķietami nekaitÄ«gu modeli ā atļaujÄm, kas balstÄ«tas uz virknÄm ā praksi, kas, lai gan sÄkotnÄji ir vienkÄrÅ”a, bieži noved pie nestabilas, kļūdÄm pakļautas un nedroÅ”as sistÄmas. Ko darÄ«t, ja mÄs varÄtu izmantot savus izstrÄdes rÄ«kus, lai atklÄtu autorizÄcijas kļūdas, pirms tÄs vispÄr sasniedz ražoÅ”anu? Ko darÄ«t, ja kompilators pats varÄtu kļūt par mÅ«su pirmo aizsardzÄ«bas lÄ«niju? Laipni lÅ«gti tipdroÅ”as autorizÄcijas pasaulÄ.
Å is ceļvedis jÅ«s aizvedÄ«s visaptveroÅ”Ä ceļojumÄ no nestabilÄs virkÅu atļauju pasaules lÄ«dz robustas, uzturamas un ļoti droÅ”as tipdroÅ”as autorizÄcijas sistÄmas izveidei. MÄs pÄtÄ«sim "kÄpÄc", "ko" un "kÄ", izmantojot praktiskus piemÄrus TypeScript, lai ilustrÄtu jÄdzienus, kas piemÄrojami jebkurai statiski tipizÄtai valodai. LÄ«dz beigÄm jÅ«s ne tikai sapratÄ«siet teoriju, bet arÄ« iegÅ«siet praktiskas zinÄÅ”anas, lai ieviestu atļauju pÄrvaldÄ«bas sistÄmu, kas stiprinÄs jÅ«su lietojumprogrammas droŔības stÄvokli un ievÄrojami uzlabos jÅ«su izstrÄdÄtÄja pieredzi.
VirkÅu atļauju nestabilitÄte: izplatÄ«ta kļūda
AutorizÄcijas pamatÄ ir vienkÄrÅ”a jautÄjuma atbildÄÅ”ana: "Vai Å”im lietotÄjam ir atļauja veikt Å”o darbÄ«bu?" VisvienkÄrÅ”Äkais veids, kÄ attÄlot atļauju, ir virkne, piemÄram, "edit_post" vai "delete_user". Tas noved pie koda, kas izskatÄs Å”Ädi:
if (user.hasPermission("create_product")) { ... }
Å Ä« pieeja sÄkotnÄji ir viegli ievieÅ”ama, taÄu tÄ ir kÄrÅ”u namiÅÅ”. Å Ä« prakse, ko bieži dÄvÄ par "maÄ£isko virkÅu" izmantoÅ”anu, rada ievÄrojamu risku un tehnisko parÄdu. IzpÄtÄ«sim, kÄpÄc Å”is modelis ir tik problemÄtisks.
Kļūdu kaskÄde
- Klusas drukas kļūdas: Å Ä« ir visredzamÄkÄ problÄma. VienkÄrÅ”a drukas kļūda, piemÄram, pÄrbaude uz
"create_pruduct", nevis"create_product", neizraisÄ«s avÄriju. TÄ pat nemetÄ«s brÄ«dinÄjumu. PÄrbaude vienkÄrÅ”i klusi neizdosies, un lietotÄjam, kuram vajadzÄtu bÅ«t piekļuvei, tiks atteikta piekļuve. VÄl ļaunÄk, drukas kļūda atļauju definÄ«cijÄ var netīŔÄm pieŔķirt piekļuvi tur, kur tai nevajadzÄtu bÅ«t. Å Ädas kļūdas ir neticami grÅ«ti izsekot. - AtklÄÅ”anas trÅ«kums: Kad jauns izstrÄdÄtÄjs pievienojas komandai, kÄ viÅÅ” zina, kuras atļaujas ir pieejamas? ViÅam jÄmeklÄ visÄ kodu bÄzÄ, cerot atrast visas lietojumprogrammas. Nav viena patiesÄ«bas avota, nav automÄtiskÄs pabeigÅ”anas un nav dokumentÄcijas, ko nodroÅ”inÄtu pats kods.
- RefaktorÄÅ”anas murgi: IedomÄjieties, ka jÅ«su organizÄcija nolemj pieÅemt strukturÄtÄku nosaukumu pieŔķirÅ”anas konvenciju, mainot
"edit_post"uz"post:update". Tas prasa globÄlu, reÄ£istrjÅ«tÄ«gu meklÄÅ”anas un aizstÄÅ”anas operÄciju visÄ kodu bÄzÄ ā aizmugurÄ, priekÅ”pusÄ un, iespÄjams, pat datu bÄzes ierakstos. Tas ir augsta riska manuÄls process, kurÄ viens nokavÄts gadÄ«jums var salauzt funkciju vai radÄ«t droŔības caurumu. - Bez kompilÄcijas laika droŔības: FundamentÄlÄ vÄjÄ«ba ir tÄda, ka atļaujas virknes derÄ«gums tiek pÄrbaudÄ«ts tikai izpildes laikÄ. Kompilatoram nav zinÄÅ”anu par to, kuras virknes ir derÄ«gas atļaujas un kuras nav. Tas uzskata
"delete_user"un"delete_useeer"par vienlÄ«dz derÄ«gÄm virknÄm, atliekot kļūdas atklÄÅ”anu jÅ«su lietotÄjiem vai jÅ«su testÄÅ”anas fÄzei.
KonkrÄts neveiksmes piemÄrs
Apsveriet aizmugures pakalpojumu, kas kontrolÄ dokumentu piekļuvi. Atļauja dzÄst dokumentu ir definÄta kÄ "document_delete".
IzstrÄdÄtÄjs, kurÅ” strÄdÄ pie administratora paneļa, vÄlas pievienot dzÄÅ”anas pogu. ViÅÅ” raksta pÄrbaudi Å”Ädi:
// API galapunktÄ
if (currentUser.hasPermission("document:delete")) {
// TurpinÄt ar dzÄÅ”anu
} else {
return res.status(403).send("Forbidden");
}
IzstrÄdÄtÄjs, sekojot jaunÄkai konvencijai, izmantoja kols (:) "_" vietÄ. Kods ir sintaktiski pareizs un izpildÄ«s visas lintinga likumu prasÄ«bas. TomÄr, kad tas tiks izvietots, neviens administrators nevarÄs dzÄst dokumentus. Funkcija ir salauzta, bet sistÄma neapstÄjas. TÄ vienkÄrÅ”i atgriež 403 Forbidden kļūdu. Å Ä« kļūda var palikt nepamanÄ«ta vairÄkas dienas vai nedÄļas, radot lietotÄju neapmierinÄtÄ«bu un pieprasot sÄpÄ«gu atkļūdoÅ”anas sesiju, lai atklÄtu viena rakstzÄ«mes kļūdu.
Tas nav ilgtspÄjÄ«gs vai droÅ”s veids, kÄ veidot profesionÄlu programmatÅ«ru. Mums ir nepiecieÅ”ama labÄka pieeja.
IepazīŔanÄs ar tipdroÅ”u autorizÄciju: kompilators kÄ jÅ«su pirmÄ aizsardzÄ«bas lÄ«nija
TipdroÅ”a autorizÄcija ir paradigmas maiÅa. TÄ vietÄ, lai attÄlotu atļaujas kÄ patvaļīgas virknes, par kurÄm kompilatoram nav nekÄdu zinÄÅ”anu, mÄs tÄs definÄjam kÄ skaidrus tipus mÅ«su programmÄÅ”anas valodas tipu sistÄmÄ. Å Ä« vienkÄrÅ”Ä izmaiÅa pÄrvieto atļauju validÄciju no izpildes laika problÄmas uz kompilÄcijas laika garantiju.
Kad izmantojat tipdroÅ”u sistÄmu, kompilators saprot visu derÄ«go atļauju kopu. Ja mÄÄ£inÄsiet pÄrbaudÄ«t neeksistÄjoÅ”u atļauju, jÅ«su kods pat netiks kompilÄts. Drukas kļūda no mÅ«su iepriekÅ”ÄjÄ piemÄra, "document:delete" pret "document_delete", tiktu uzreiz atklÄta jÅ«su koda redaktorÄ, pasvÄ«trota sarkanÄ krÄsÄ, pirms jÅ«s pat saglabÄjat failu.
Pamatprincipi
- CentralizÄta definÄ«cija: Visas iespÄjamÄs atļaujas ir definÄtas vienÄ, kopÄ«gÄ vietÄ. Å is fails vai modulis kļūst par nenoliedzamu patiesÄ«bas avotu visai lietojumprogrammas droŔības modelim.
- KompilÄcijas laika pÄrbaude: Tipu sistÄma nodroÅ”ina, ka jebkura atsauce uz atļauju, vai tÄ bÅ«tu pÄrbaudÄ, lomu definÄ«cijÄ vai UI komponentÄ, ir derÄ«ga, eksistÄjoÅ”a atļauja. Drukas kļūdas un neeksistÄjoÅ”as atļaujas ir neiespÄjamas.
- Uzlabota izstrÄdÄtÄju pieredze (DX): IzstrÄdÄtÄji saÅem IDE funkcijas, piemÄram, automÄtisko pabeigÅ”anu, rakstot
user.hasPermission(...). ViÅi var redzÄt visu pieejamo atļauju nolaižamo sarakstu, padarot sistÄmu paÅ”dokumentÄjoÅ”u un samazinot garÄ«go slogu atcerÄties precÄ«zas virkÅu vÄrtÄ«bas. - PÄrliecinoÅ”a refaktorÄÅ”ana: Ja jums ir jÄpÄrsauc atļauja, varat izmantot savas IDE iebÅ«vÄtos refaktorÄÅ”anas rÄ«kus. Atļaujas pÄrsaukÅ”ana tÄs avotÄ automÄtiski un droÅ”i atjauninÄs katru lietojumu visÄ projektÄ. Tas, kas kÄdreiz bija augsta riska manuÄls uzdevums, kļūst par triviÄlu, droÅ”u un automatizÄtu uzdevumu.
Pamatu veidoÅ”ana: tipdroÅ”as atļauju sistÄmas ievieÅ”ana
PÄriesim no teorijas uz praksi. MÄs izveidosim pilnÄ«gu, tipdroÅ”u atļauju sistÄmu no paÅ”a sÄkuma. MÅ«su piemÄriem mÄs izmantosim TypeScript, jo tÄ jaudÄ«gÄ tipu sistÄma ir lieliski piemÄrota Å”im uzdevumam. TomÄr pamatprincipus var viegli pielÄgot citÄm statiski tipizÄtÄm valodÄm, piemÄram, C#, Java, Swift, Kotlin vai Rust.
1. solis: Atļauju definÄÅ”ana
Pirmais un vissvarÄ«gÄkais solis ir izveidot vienu patiesÄ«bas avotu visÄm atļaujÄm. To var panÄkt vairÄkos veidos, katram no kuriem ir savas priekÅ”rocÄ«bas un trÅ«kumi.
A variants: VirkÅu literÄļu apvienoto tipu izmantoÅ”ana
Å Ä« ir vienkÄrÅ”ÄkÄ pieeja. JÅ«s definÄjat tipu, kas ir visu iespÄjamo atļauju virkÅu apvienojums. Tas ir kodolÄ«gs un efektÄ«vs mazÄkÄm lietojumprogrammÄm.
// src/permissions.ts
export type Permission =
| "user:create"
| "user:read"
| "user:update"
| "user:delete"
| "post:create"
| "post:read"
| "post:update"
| "post:delete";
PriekÅ”rocÄ«bas: Ä»oti vienkÄrÅ”i rakstÄms un saprotams.
TrÅ«kumi: Var kļūt nepÄrskatÄms, palielinoties atļauju skaitam. Tas nenodroÅ”ina veidu, kÄ grupÄt saistÄ«tas atļaujas, un, tÄs lietojot, jums joprojÄm ir jÄievada virknes.
B variants: Uzskaitījumu izmantoŔana
UzskaitÄ«jumi nodroÅ”ina veidu, kÄ grupÄt saistÄ«tÄs konstantes zem viena nosaukuma, kas var padarÄ«t jÅ«su kodu lasÄmÄku.
// src/permissions.ts
export enum Permission {
UserCreate = "user:create",
UserRead = "user:read",
UserUpdate = "user:update",
UserDelete = "user:delete",
PostCreate = "post:create",
// ... un tÄ tÄlÄk
}
PriekÅ”rocÄ«bas: NodroÅ”ina nosauktas konstantes (Permission.UserCreate), kas var novÄrst drukas kļūdas, lietojot atļaujas.
TrÅ«kumi: TypeScript uzskaitÄ«jumiem ir dažas nianses un tie var bÅ«t mazÄk elastÄ«gi nekÄ citas pieejas. VirkÅu vÄrtÄ«bu iegūŔana apvienotajam tipam prasa papildu soli.
C variants: Objekts kÄ konstantes pieeja (ieteicams)
Å Ä« ir visspÄcÄ«gÄkÄ un mÄrogojamÄkÄ pieeja. MÄs definÄjam atļaujas dziļi ligzdotÄ, tikai lasÄmÄ objektÄ, izmantojot TypeScript `as const` apgalvojumu. Tas mums nodroÅ”ina labÄko no visÄm pasaulÄm: organizÄciju, atklÄjamÄ«bu, izmantojot punktu pierakstu (piemÄram, Permissions.USER.CREATE), un spÄju dinamiski Ä£enerÄt visu atļauju virkÅu apvienoto tipu.
LÅ«k, kÄ to iestatÄ«t:
// src/permissions.ts
// 1. DefinÄjiet atļauju objektu ar 'as const'
export const Permissions = {
USER: {
CREATE: "user:create",
READ: "user:read",
UPDATE: "user:update",
DELETE: "user:delete",
},
POST: {
CREATE: "post:create",
READ: "post:read",
UPDATE: "post:update",
DELETE: "post:delete",
},
BILLING: {
READ_INVOICES: "billing:read_invoices",
MANAGE_SUBSCRIPTION: "billing:manage_subscription",
}
} as const;
// 2. Izveidojiet palÄ«gtipu, lai iegÅ«tu visas atļauju vÄrtÄ«bas
type TPermissions = typeof Permissions;
// Å is utilÄ«ta tips rekursÄ«vi sapludina ligzdoto objektu vÄrtÄ«bas apvienotÄ tipÄ
type FlattenObjectValues<T> = T extends object ? FlattenObjectValues<T[keyof T]> : T;
// 3. Izveidojiet visu atļauju gala apvienoto tipu
export type AllPermissions = FlattenObjectValues<TPermissions>;
// Ä¢enerÄtais 'AllPermissions' tips bÅ«s:
// "user:create" | "user:read" | "user:update" | "user:delete" | ... un tÄ tÄlÄk
Å Ä« pieeja ir labÄka, jo tÄ nodroÅ”ina skaidru, hierarhisku struktÅ«ru jÅ«su atļaujÄm, kas ir ļoti svarÄ«gi, jÅ«su lietojumprogrammai augot. To ir viegli pÄrlÅ«kot, un tips AllPermissions tiek Ä£enerÄts automÄtiski, kas nozÄ«mÄ, ka jums nekad nav manuÄli jÄatjaunina apvienots tips. Tas ir pamats, ko mÄs izmantosim pÄrÄjai mÅ«su sistÄmai.
2. solis: Lomu definÄÅ”ana
Loma ir vienkÄrÅ”i nosaukta atļauju kolekcija. Tagad mÄs varam izmantot mÅ«su AllPermissions tipu, lai nodroÅ”inÄtu, ka arÄ« mÅ«su lomu definÄ«cijas ir tipdroÅ”as.
// src/roles.ts
import { Permissions, AllPermissions } from './permissions';
// DefinÄjiet lomas struktÅ«ru
export type Role = {
name: string;
description: string;
permissions: AllPermissions[];
};
// DefinÄjiet visu lietojumprogrammas lomu ierakstu
export const AppRoles: Record<string, Role> = {
GUEST: {
name: 'Viesis',
description: 'Ierobežota piekļuve anonÄ«miem lietotÄjiem.',
permissions: [
Permissions.POST.READ, // Var lasÄ«t ziÅas
Permissions.USER.READ, // Var skatÄ«t lietotÄju profilus
],
},
EDITOR: {
name: 'Redaktors',
description: 'Var izveidot un pÄrvaldÄ«t savu saturu.',
permissions: [
Permissions.POST.READ,
Permissions.POST.CREATE,
Permissions.POST.UPDATE, // PiezÄ«me: Tas nenorÄda, *kuru* ziÅu. To mÄs risinÄsim vÄlÄk.
Permissions.POST.DELETE,
Permissions.USER.READ,
],
},
ADMIN: {
name: 'Administrators',
description: 'Pilna piekļuve visÄm sistÄmas funkcijÄm.',
// Dinamiski pieŔķirt visas atļaujas administratoram
permissions: Object.values(Permissions).flatMap(resource => Object.values(resource)),
},
};
// MÄs varam arÄ« izveidot tipu mÅ«su lomu atslÄgÄm papildu droŔībai
export type AppRoleKey = keyof typeof AppRoles; // "GUEST" | "EDITOR" | "ADMIN"
IevÄrojiet, kÄ mÄs izmantojam Permissions objektu (piemÄram, Permissions.POST.READ), lai pieŔķirtu atļaujas. Tas novÄrÅ” drukas kļūdas un nodroÅ”ina, ka mÄs pieŔķiram tikai derÄ«gas atļaujas. ADMIN lomai mÄs programmatiski saplacinÄm mÅ«su Permissions objektu, lai pieŔķirtu katru atļauju, nodroÅ”inot, ka, pievienojot jaunas atļaujas, administratori tÄs automÄtiski pÄrmanto.
3. solis: TipdroÅ”as pÄrbaudes funkcijas izveide
Tas ir mÅ«su sistÄmas stÅ«rakmens. Mums ir nepiecieÅ”ama funkcija, kas var pÄrbaudÄ«t, vai lietotÄjam ir noteikta atļauja. Galvenais ir funkcijas paraksts, kas nodroÅ”inÄs, ka var pÄrbaudÄ«t tikai derÄ«gas atļaujas.
Vispirms definÄsim, kÄds varÄtu izskatÄ«ties User objekts:
// src/user.ts
import { AppRoleKey } from './roles';
export type User = {
id: string;
email: string;
roles: AppRoleKey[]; // LietotÄja lomas arÄ« ir tipdroÅ”as!
};
Tagad veidosim autorizÄcijas loÄ£iku. EfektivitÄtes labad vislabÄk ir vienu reizi aprÄÄ·inÄt lietotÄja pilnu atļauju kopu un pÄc tam pÄrbaudÄ«t pret Å”o kopu.
// src/authorization.ts
import { User } from './user';
import { AppRoles } from './roles';
import { AllPermissions } from './permissions';
/**
* AprÄÄ·ina pilnu atļauju kopu dotajam lietotÄjam.
* Izmanto Set, lai nodroÅ”inÄtu efektÄ«vu O(1) meklÄÅ”anu.
* @param user LietotÄja objekts.
* @returns Kopu, kas satur visas lietotÄja atļaujas.
*/
function getUserPermissions(user: User): Set<AllPermissions> {
const permissionSet = new Set<AllPermissions>();
user.roles.forEach(roleKey => {
const role = AppRoles[roleKey];
if (role) {
role.permissions.forEach(permission => {
permissionSet.add(permission);
});
}
});
return permissionSet;
}
/**
* PamatÄ«ga tipdroÅ”a funkcija, lai pÄrbaudÄ«tu, vai lietotÄjam ir noteikta atļauja.
* @param user PÄrbaudÄmais lietotÄjs.
* @param permission PÄrbaudÄmÄ atļauja. JÄbÅ«t derÄ«gam AllPermissions tipam.
* @returns True, ja lietotÄjam ir atļauja, false citÄdi.
*/
export function hasPermission(
user: User | null,
permission: AllPermissions
): boolean {
if (!user) {
return false;
}
const userPermissions = getUserPermissions(user);
return userPermissions.has(permission);
}
BurvÄ«ba slÄpjas funkcijas hasPermission parametra permission: AllPermissions. Å is paraksts norÄda TypeScript kompilatoram, ka otrajam argumentam jÄbÅ«t vienai no virknÄm no mÅ«su Ä£enerÄtÄ AllPermissions apvienotÄ tipa. JebkurÅ” mÄÄ£inÄjums izmantot citu virkni radÄ«s kompilÄcijas laika kļūdu.
LietoÅ”ana praksÄ
ApskatÄ«sim, kÄ tas maina mÅ«su ikdienas kodÄÅ”anu. IedomÄjieties, ka aizsargÄjat API galapunktu Node.js/Express lietojumprogrammÄ:
import { hasPermission } from './authorization';
import { Permissions } from './permissions';
import { User } from './user';
app.delete('/api/posts/:id', (req, res) => {
const currentUser: User = req.user; // PieÅemam, ka lietotÄjs ir pievienots no autentifikÄcijas starpprogrammatÅ«ras
// Tas darbojas perfekti! MÄs saÅemam automÄtisko pabeigÅ”anu priekÅ” Permissions.POST.DELETE
if (hasPermission(currentUser, Permissions.POST.DELETE)) {
// LoÄ£ika ziÅas dzÄÅ”anai
res.status(200).send({ message: 'ZiÅa dzÄsta.' });
} else {
res.status(403).send({ error: 'Jums nav atļaujas dzÄst ziÅas.' });
}
});
// Tagad mÄÄ£inÄsim pieļaut kļūdu:
app.post('/api/users', (req, res) => {
const currentUser: User = req.user;
// NÄkamÄ rinda parÄdÄ«s sarkanu viļÅveida lÄ«niju jÅ«su IDE un NEKOMPILÄSIES!
// Kļūda: tipa '"user:creat"' arguments nav pieŔķirams tipa 'AllPermissions' parametram.
// Vai jÅ«s domÄjÄt '"user:create"'?
if (hasPermission(currentUser, "user:creat")) { // Drukas kļūda vÄrdÄ 'create'
// Å is kods nav sasniedzams
}
});
MÄs esam veiksmÄ«gi likvidÄjuÅ”i veselu kļūdu kategoriju. Kompilators tagad ir aktÄ«vs dalÄ«bnieks mÅ«su droŔības modeļa ievieÅ”anÄ.
SistÄmas mÄrogoÅ”ana: progresÄ«vi jÄdzieni tipdroÅ”Ä autorizÄcijÄ
VienkÄrÅ”a uz lomÄm balstÄ«ta piekļuves kontroles (RBAC) sistÄma ir jaudÄ«ga, taÄu reÄlÄs pasaules lietojumprogrammÄm bieži vien ir sarežģītÄkas vajadzÄ«bas. KÄ mÄs rÄ«kojamies ar atļaujÄm, kas ir atkarÄ«gas no paÅ”iem datiem? PiemÄram, EDITOR var atjauninÄt ziÅu, bet tikai savu ziÅu.
Uz atribūtiem balstīta piekļuves kontrole (ABAC) un uz resursiem balstītas atļaujas
Å eit mÄs ievieÅ”am uz atribÅ«tiem balstÄ«tas piekļuves kontroles (ABAC) jÄdzienu. MÄs paplaÅ”inÄm savu sistÄmu, lai apstrÄdÄtu politikas vai nosacÄ«jumus. LietotÄjam ir jÄbÅ«t ne tikai vispÄrÄjai atļaujai (piemÄram, post:update), bet arÄ« jÄatbilst noteikumam, kas saistÄ«ts ar konkrÄto resursu, kuram viÅÅ” cenÅ”as piekļūt.
To mÄs varam modelÄt, izmantojot uz politikÄm balstÄ«tu pieeju. MÄs definÄjam politiku karti, kas atbilst noteiktÄm atļaujÄm.
// src/policies.ts
import { User } from './user';
// DefinÄjiet mÅ«su resursu tipus
type Post = { id: string; authorId: string; };
// DefinÄjiet politiku karti. AtslÄgas ir mÅ«su tipdroÅ”as atļaujas!
type PolicyMap = {
[Permissions.POST.UPDATE]?: (user: User, post: Post) => boolean;
[Permissions.POST.DELETE]?: (user: User, post: Post) => boolean;
// Citas politikas...
};
export const policies: PolicyMap = {
[Permissions.POST.UPDATE]: (user, post) => {
// Lai atjauninÄtu ziÅu, lietotÄjam jÄbÅ«t autoram.
return user.id === post.authorId;
},
[Permissions.POST.DELETE]: (user, post) => {
// Lai dzÄstu ziÅu, lietotÄjam jÄbÅ«t autoram.
return user.id === post.authorId;
},
};
// MÄs varam izveidot jaunu, jaudÄ«gÄku pÄrbaudes funkciju
export function can(user: User | null, permission: AllPermissions, resource?: any): boolean {
if (!user) return false;
// 1. Vispirms pÄrbaudiet, vai lietotÄjam ir pamatatļauja no viÅa lomas.
if (!hasPermission(user, permission)) {
return false;
}
// 2. TÄlÄk pÄrbaudiet, vai Å”ai atļaujai pastÄv Ä«paÅ”a politika.
const policy = policies[permission];
if (policy) {
// 3. Ja politika pastÄv, tÄ ir jÄizpilda.
if (!resource) {
// Politika prasa resursu, bet neviens netika nodroÅ”inÄts.
console.warn(`Politika attiecÄ«bÄ uz ${permission} netika pÄrbaudÄ«ta, jo netika nodroÅ”inÄts resurss.`);
return false;
}
return policy(user, resource);
}
// 4. Ja politika nepastÄv, ar lomu balstÄ«tu atļauju pietiek.
return true;
}
Tagad mÅ«su API galapunkts kļūst niansÄtÄks un droÅ”Äks:
import { can } from './policies';
import { Permissions } from './permissions';
app.put('/api/posts/:id', async (req, res) => {
const currentUser = req.user;
const post = await db.posts.findById(req.params.id);
// PÄrbaudiet spÄju atjauninÄt Å”o *konkrÄto* ziÅu
if (can(currentUser, Permissions.POST.UPDATE, post)) {
// LietotÄjam ir 'post:update' atļauja UN viÅÅ” ir autors.
// TurpinÄt ar atjauninÄÅ”anas loÄ£iku...
} else {
res.status(403).send({ error: 'Jums nav atļaujas atjauninÄt Å”o ziÅu.' });
}
});
Frontend integrÄcija: Tipu koplietoÅ”ana starp backend un frontend
Viena no Ŕīs pieejas bÅ«tiskÄkajÄm priekÅ”rocÄ«bÄm, jo Ä«paÅ”i, ja TypeScript tiek izmantots gan priekÅ”pusÄ, gan aizmugurÄ, ir spÄja koplietot Å”os tipus. Izvietojot failus permissions.ts, roles.ts un citus koplietojamos failus kopÄ«gÄ paketÄ monorepo (izmantojot tÄdus rÄ«kus kÄ Nx, Turborepo vai Lerna), jÅ«su priekÅ”ÄjÄs daļas lietojumprogramma pilnÄ«bÄ apzinÄs autorizÄcijas modeli.
Tas ļauj jÅ«su UI kodÄ izmantot jaudÄ«gus modeļus, piemÄram, nosacÄ«ti atveidot elementus, pamatojoties uz lietotÄja atļaujÄm, un tas viss notiek ar tipu sistÄmas droŔību.
Apsveriet React komponentu:
// React komponentÄ
import { Permissions } from '@my-app/shared-types'; // ImportÄÅ”ana no koplietojamas pakotnes
import { useAuth } from './auth-context'; // PielÄgots ÄÄ·is autentifikÄcijas stÄvoklim
interface EditPostButtonProps {
post: Post;
}
const EditPostButton = ({ post }: EditPostButtonProps) => {
const { user, can } = useAuth(); // 'can' ir ÄÄ·is, kas izmanto mÅ«su jauno uz politikÄm balstÄ«to loÄ£iku
// PÄrbaude ir tipdroÅ”a. UI zina par atļaujÄm un politikÄm!
if (!can(user, Permissions.POST.UPDATE, post)) {
return null; // Pat neatveidot pogu, ja lietotÄjs nevar veikt darbÄ«bu
}
return <button>RediÄ£Ät ziÅu</button>;
};
Tas ir spÄles pavÄrsiens. JÅ«su priekÅ”ÄjÄs daļas kodam vairs nav jÄmin vai jÄizmanto cieti kodÄtas virknes, lai kontrolÄtu UI redzamÄ«bu. Tas ir perfekti sinhronizÄts ar aizmugures droŔības modeli, un jebkÄdas izmaiÅas atļaujÄs aizmugurÄ nekavÄjoties radÄ«s tipu kļūdas priekÅ”pusÄ, ja tÄs netiks atjauninÄtas, novÄrÅ”ot UI neatbilstÄ«bas.
Biznesa pamatojums: KÄpÄc jÅ«su organizÄcijai jÄinvestÄ tipdroÅ”Ä autorizÄcijÄ
Å Ä« modeļa pieÅemÅ”ana ir vairÄk nekÄ tikai tehnisks uzlabojums; tÄ ir stratÄÄ£iska investÄ«cija ar taustÄmiem biznesa ieguvumiem.
- IevÄrojami samazinÄtas kļūdas: NovÄrÅ” veselu droŔības ievainojamÄ«bu un izpildes laika kļūdu klasi, kas saistÄ«tas ar autorizÄciju. Tas nozÄ«mÄ stabilÄku produktu un mazÄk dÄrgu ražoÅ”anas incidentu.
- PaÄtrinÄta izstrÄdes Ätrums: AutomÄtiskÄ pabeigÅ”ana, statiskÄ analÄ«ze un paÅ”dokumentÄjoÅ”s kods padara izstrÄdÄtÄjus ÄtrÄkus un pÄrliecinÄtÄkus. MazÄk laika tiek pavadÄ«ts, meklÄjot atļauju virknes vai atkļūdojot klusas autorizÄcijas kļūmes.
- VienkÄrÅ”ota ievieÅ”ana un uzturÄÅ”ana: Atļauju sistÄma vairs nav cilts zinÄÅ”anas. Jaunie izstrÄdÄtÄji var nekavÄjoties saprast droŔības modeli, pÄrbaudot koplietotos tipus. UzturÄÅ”ana un refaktorÄÅ”ana kļūst par zema riska, paredzamiem uzdevumiem.
- Uzlabota droŔības pozÄ«cija: Skaidra, skaidri formulÄta un centralizÄti pÄrvaldÄ«ta atļauju sistÄma ir daudz vieglÄk auditÄjama un saprotama. Kļūst triviÄli atbildÄt uz jautÄjumiem, piemÄram, "Kuram ir atļauja dzÄst lietotÄjus?" Tas stiprina atbilstÄ«bu un droŔības pÄrbaudes.
IzaicinÄjumi un apsvÄrumi
Lai gan Ŕī pieeja ir jaudÄ«ga, tai ir arÄ« savi apsvÄrumi:
- SÄkotnÄjÄs iestatīŔanas sarežģītÄ«ba: Tas prasa vairÄk sÄkotnÄjo arhitektÅ«ras pÄrdomu nekÄ vienkÄrÅ”i virkÅu pÄrbaužu izkliedÄÅ”ana visÄ jÅ«su kodÄ. TomÄr Å”is sÄkotnÄjais ieguldÄ«jums atmaksÄjas visÄ projekta dzÄ«ves ciklÄ.
- VeiktspÄja mÄrogÄ: SistÄmÄs ar tÅ«kstoÅ”iem atļauju vai ÄrkÄrtÄ«gi sarežģītÄm lietotÄju hierarhijÄm lietotÄja atļauju kopas aprÄÄ·inÄÅ”anas process (
getUserPermissions) var kļūt par vÄjo vietu. Å Ädos gadÄ«jumos ir ļoti svarÄ«gi ieviest keÅ”atmiÅas stratÄÄ£ijas (piemÄram, izmantojot Redis, lai saglabÄtu aprÄÄ·inÄtÄs atļauju kopas). - RÄ«ku un valodu atbalsts: Å Ä«s pieejas pilnÄ«gie ieguvumi tiek realizÄti valodÄs ar spÄcÄ«gÄm statiskÄs tipizÄcijas sistÄmÄm. Lai gan to ir iespÄjams aptuveni Ä«stenot dinamiski tipizÄtÄs valodÄs, piemÄram, Python vai Ruby, izmantojot tipu norÄdes un statiskÄs analÄ«zes rÄ«kus, tÄ vislabÄk ir piemÄrota tÄdÄm valodÄm kÄ TypeScript, C#, Java un Rust.
SecinÄjums: DroÅ”Äkas un uzturamÄkas nÄkotnes veidoÅ”ana
MÄs esam ceļojuÅ”i no maÄ£isko virkÅu bÄ«stamÄs ainavas uz tipdroÅ”as autorizÄcijas labi nocietinÄto pilsÄtu. Apsverot atļaujas nevis kÄ vienkÄrÅ”us datus, bet gan kÄ mÅ«su lietojumprogrammas tipu sistÄmas pamatu, mÄs pÄrveidojam kompilatoru no vienkÄrÅ”a koda pÄrbaudÄ«tÄja par modru droŔības sargu.
TipdroÅ”a autorizÄcija ir apliecinÄjums mÅ«sdienu programmatÅ«ras inženierijas principam, kas paredz kļūdu atklÄÅ”anu pÄc iespÄjas agrÄk izstrÄdes dzÄ«ves ciklÄ. TÄ ir stratÄÄ£iska investÄ«cija koda kvalitÄtÄ, izstrÄdÄtÄju produktivitÄtÄ un, pats galvenais, lietojumprogrammu droŔībÄ. Izveidojot sistÄmu, kas ir paÅ”dokumentÄjoÅ”a, viegli refaktorÄjama un kuru nav iespÄjams ļaunprÄtÄ«gi izmantot, jÅ«s ne tikai rakstÄt labÄku kodu; jÅ«s veidojat droÅ”Äku un uzturamÄku nÄkotni savai lietojumprogrammai un savai komandai. NÄkamreiz, kad sÄksiet jaunu projektu vai vÄlÄsities refaktorÄt vecu, pajautÄjiet sev: vai jÅ«su autorizÄcijas sistÄma strÄdÄ jÅ«su labÄ, vai pret jums?